﻿using LitJson;
using System.Collections.Generic;
using System.IO;
using DG.Tweening;
using Sliver.Roadnet.Components;
using Sliver.Roadnet.RawInfo;
using Sliver.Utility;
using UnityEngine;
using System.Runtime.InteropServices;
using System;
using static Sliver.Core.SliverEngineBridge;

namespace Sliver.Roadnet
{
    public class RoadnetEntity
    {
        public static RoadnetEntity Instance;

        // 车辆信息共享内存
        protected IntPtr RunningVehicles;

        // 删除车辆缓存共享内存
        protected IntPtr RemovedVehicles;

        // 车道信息共享内存
        protected IntPtr LaneVehicleCountBuffer;

        // 交叉口信息共享内存
        protected IntPtr RawIntersections;

        public readonly int LaneCount;
        public readonly IntPtr RawLaneInfosPtr;

        public readonly List<RawRoadInfo> RoadInfoList = new List<RawRoadInfo>();
        public readonly List<RawLaneJsonData> LaneInfoList = new List<RawLaneJsonData>();
        public readonly Dictionary<string, RawRoadInfo> RoadInfoMap = new Dictionary<string, RawRoadInfo>();

        public int IntersectionCount { get; private set; }
        public int RoadCount { get; private set; }

        public readonly List<IntersectionJsonData> IntersectionInfoList = new List<IntersectionJsonData>();
        public readonly Dictionary<string, IntersectionJsonData> IntersectionInfoMap = new Dictionary<string, IntersectionJsonData>();
        public readonly List<IntersectionEntity> IntersectionEntities = new List<IntersectionEntity>();
        public readonly Dictionary<IntersectionJsonData, IntersectionEntity> IntersectionMap = new Dictionary<IntersectionJsonData, IntersectionEntity>();

        public readonly List<RawIntersectionInfo> CacheIntersectionInfos = new List<RawIntersectionInfo>();
 
        public readonly Dictionary<string, RoadEntity> RoadEntityMap = new Dictionary<string, RoadEntity>();

        // 初始化道路的哈希集合
        public HashSet<string> InitRoadSet = new HashSet<string>();

        public static readonly IntersectionEntity IntersectionCenter;
        public static readonly GameObject RoadSegment;
        public static readonly GameObject TestCube;

        public static RoadEntity RoadEntityPrototype;
        public static RoadLinkEntity RoadLinkPrototype;
        public static LanelinkEntity LanelinkEntityPrototype;
        public static MeshRenderer NavigateArrow;
        public static PhaseLight PhaseLightPrototype;

        protected static int PtrSize = Marshal.SizeOf<IntPtr>();

        static RoadnetEntity()
        {
            IntersectionCenter = Resources.Load<IntersectionEntity>("Roadnet/Intersection");
            RoadSegment = Resources.Load<GameObject>("Roadnet/Road Segment");
            TestCube = Resources.Load<GameObject>("Roadnet/Test Cube");

            RoadEntityPrototype = Resources.Load<RoadEntity>("Roadnet/Road Entity");
            RoadLinkPrototype = Resources.Load<RoadLinkEntity>("Roadnet/Road Link");
            LanelinkEntityPrototype = Resources.Load<LanelinkEntity>("Roadnet/Lanelink");
            NavigateArrow = Resources.Load<MeshRenderer>("Roadnet/Navigate Arrow");
            PhaseLightPrototype = Resources.Load<PhaseLight>("Roadnet/phase_light");
        }

        protected Transform CameraCenter;

        public RoadnetEntity(string path, MemoryInfo memoryInfo, Transform cameraCenter, Transform roadnetTransform)
        {
            Instance = this;
            CameraCenter = cameraCenter;

            RunningVehicles = memoryInfo.running_buffer;
            RemovedVehicles = memoryInfo.removed_buffer;
            LaneVehicleCountBuffer = memoryInfo.lane_vehicle_count;
            RawIntersections = memoryInfo.raw_intersections;
            LaneCount = memoryInfo.lane_count;
            Debug.Log($"lane count: {LaneCount}");
            RawLaneInfosPtr = memoryInfo.raw_lanes;

            Generate(File.ReadAllText(path), roadnetTransform);
        }

        protected void Generate(string roadnet, Transform parent)
        {
            RawRoadNetInfo rawRoadNet = JsonMapper.ToObject<RawRoadNetInfo>(roadnet);

            IntersectionCount = rawRoadNet.intersections.Count;
            RoadCount = rawRoadNet.roads.Count;

            for (int i = 0; i < rawRoadNet.roads.Count; i++){
                RawRoadInfo road = rawRoadNet.roads[i];
                RoadInfoList.Add(road);
                RoadInfoMap.Add(road.id, road);

                for (int j = 0; j < road.lanes.Count; j++)
                {
                    RawLaneJsonData laneInfo = road.lanes[j];
                    laneInfo.id = $"{road.id}_{j}";
                    LaneInfoList.Add(laneInfo);
                }
            }

            for (int i = 0; i < IntersectionCount; i++)
            {
                IntPtr ptr = Marshal.PtrToStructure<IntPtr>(RawIntersections + i * PtrSize);
                RawIntersectionInfo rawInfo = Marshal.PtrToStructure<RawIntersectionInfo>(ptr);
                CacheIntersectionInfos.Add(rawInfo);

                IntersectionJsonData intersection = rawRoadNet.intersections[i];
                IntersectionEntity entity = GameObject.Instantiate(IntersectionCenter.gameObject,
                    new Vector3(intersection.point.x, 0, intersection.point.y),
                    Quaternion.identity).GetComponent<IntersectionEntity>();

                entity.Setup(this, intersection, i, parent);

                IntersectionInfoList.Add(intersection);
                IntersectionInfoMap.Add(intersection.id, intersection);

                IntersectionEntities.Add(entity);
                IntersectionMap.Add(intersection, entity);

                GenerateRoadLinks(intersection);
            }

            for (int i = 0; i < RoadInfoList.Count; i++)
            {
                RawRoadInfo road = RoadInfoList[i];
                Transform start = GetIntersectionUnsafe(road.startIntersection).transform;
                Transform end = GetIntersectionUnsafe(road.endIntersection).transform;

                string k1 = $"{road.startIntersection} -> {road.endIntersection}";
                string k2 = $"{road.endIntersection} -> {road.startIntersection}";

                if (InitRoadSet.Contains(k1)) continue;
                InitRoadSet.Add(k1);
                InitRoadSet.Add(k2);

                Vector3 direction = end.position - start.position;
                const float segmentLength = 30f;
                for (int j = 0; j < (direction.magnitude - segmentLength * 2) / segmentLength; j++)
                {
                    GameObject o = GameObject.Instantiate(RoadSegment, start.position, Quaternion.identity);
                    o.name = k1;
                    Transform t = o.transform;
                    // t.localScale = Vector3.one * 2;
                    // t.Translate(new Vector3(-10f, 0, 10f), Space.Self);
                    t.LookAt(direction);
                    t.Translate(Vector3.forward * segmentLength * j, Space.Self);
                    t.Translate(-new Vector3(0, 0, -60f), Space.Self);
                }

                RoadEntity entity = GameObject.Instantiate(RoadEntityPrototype, parent).GetComponent<RoadEntity>();
                entity.Setup(road, i, parent);
                RoadEntityMap.Add(road.id, entity);
            }

            // 等待道路与交叉口都初始化完成

            for (int i = 0; i < IntersectionCount; i++)
            {
                IntersectionJsonData intersection = rawRoadNet.intersections[i];
                IntersectionEntities[i].GenerateLight(this, intersection);
            }
        }

        public void GenerateRoadLinks(IntersectionJsonData intersection)
        {
            IntersectionEntity entity = IntersectionMap[intersection];
            entity.ShowLaneLinks();

            // 绘制roadlink
        }

        public IntersectionEntity GetIntersectionUnsafe(string id)
        {
            return IntersectionMap[IntersectionInfoMap[id]];
        }

        public IntersectionEntity GetIntersection(string id)
        {
            if (IntersectionInfoMap.TryGetValue(id, out var info))
            {
                return GetIntersection(info);
            }
            else
            {
                return null;
            }
        }

        public IntersectionEntity GetIntersection(IntersectionJsonData jsonData)
        {
            return IntersectionMap.TryGetValue(jsonData, out var intersection) ? intersection : null;
        }

        public Dictionary<int, RawDrivableInfo> GetLaneInfos()
        {
            Dictionary<int, RawDrivableInfo> result = new Dictionary<int, RawDrivableInfo>();
            for (int i = 0; i < LaneInfoList.Count; i++)
            {
                RawDrivableInfo laneInfo = Marshal.PtrToStructure<RawDrivableInfo>(RawLaneInfosPtr + i * Marshal.SizeOf<RawDrivableInfo>());
                result[laneInfo.id] = laneInfo;
            }
            return result;
        }

        public Dictionary<int, int> GetLaneRunningCount()
        {
            Dictionary<int, int> result = new Dictionary<int, int>();
            for (int i = 0; i < LaneCount; i++)
            {
                RawDrivableInfo laneInfo = Marshal.PtrToStructure<RawDrivableInfo>(RawLaneInfosPtr + i * Marshal.SizeOf<RawDrivableInfo>());
                result[laneInfo.id] = laneInfo.r_c;
            }
            return result;
        }

        public IntersectionJsonData WatchIntersection(int index)
        {
            index %= IntersectionInfoList.Count;
            IntersectionJsonData info = IntersectionInfoList[index];
            CameraCenter.DOMove(info.point.xz3(), 0.25f);
            return info;
        }

        public void Update()
        {
            UpdateCache();
        }

        public void UpdateCache()
        {
            for (int i = 0; i < IntersectionCount; i++)
            {
                IntPtr ptr = Marshal.PtrToStructure<IntPtr>(RawIntersections + i * PtrSize);
                RawIntersectionInfo rawInfo = Marshal.PtrToStructure<RawIntersectionInfo>(ptr);

                if (rawInfo.PhaseIndex != CacheIntersectionInfos[i].PhaseIndex)
                {
                    Debug.Log("--------------");
                    IntersectionEntities[i].ChangeLightPhase(rawInfo.PhaseIndex);
                }

                CacheIntersectionInfos[i] = rawInfo;
            }
        }

        public static void OnLightPhaseChanged(string id, int[] phases, int length)
        {

        }
    }
}
